home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 426-450 / disk_436 / keymacro / mrarpfile.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  17KB  |  691 lines

  1. /*  File support routines to complement ARP.
  2.  *  Author: Mark R. Rinfret
  3.  *          Usenet:     mrr@amanpt1.Newport.RI.US
  4.  *          BIX:        markr
  5.  *          CIS:        72017, 136 (good luck!)
  6.  *
  7.  *          348 Indian Avenue
  8.  *          Portsmouth, RI 02871
  9.  *          401-846-7639 (home)
  10.  *          401-849-9390 (work)       
  11.  *
  12.  *  This package was written primarily because of _one_ missing element
  13.  *  in ARP: FGets. ARGH! ARPFFFT! It extends ARP by adding buffering to
  14.  *  the basic file type (FileHandle) and defining a new type, named
  15.  *  ARPFileHandle (hope this is OK with the ARP guys). Also, I've used the
  16.  *  convention of embedding ARP (vs. Arp in MicroSmith's stuff) in all type
  17.  *  and function names to (hopefully) avoid naming collisions.
  18.  *
  19.  *  This package (as far as I am concerned) is public domain. Use it any
  20.  *  way you like. Some comments on the "MR" prefix: it isn't short for
  21.  *  "Mister", it comprises the initials of my first and last names;
  22.  *  I use it primarily to avoid name collisions with stuff by other
  23.  *  authors. If any other authors whose initials are "MR" start doing
  24.  *  this, we'll probably have to appeal to Electronic Arts to dole out
  25.  *  "author codes" along the lines of those issued for IFF :-). I hereby
  26.  *  stake a claim to 'MRR_'.
  27.  *
  28.  *  A word of warning:
  29.  *
  30.  *  DO NOT INTERMIX STANDARD AMIGADOS FILE SUPPORT CALLS WITH CALLS TO
  31.  *  THIS PACKAGE ON FILES USING THIS FILE METHOD!
  32.  *
  33.  *  Obviously, the system doesn't know about the buffering added here
  34.  *  and will cause unpredictable results (unless, of course, you
  35.  *  take care to maintain the ARPFileHandle information).
  36.  */
  37.  
  38. /*  Olsen: In order to adapt the code to ANSI some declarations and
  39.  *         function calls were rearranged. I also fixed two parts of
  40.  *         code which appeared not to work correctly.
  41.  */
  42.  
  43.     /* Skip prototypes. */
  44.  
  45. #define _MRARPFILE_PRIVATE 1
  46.  
  47. #include "MRARPFile.h"
  48.  
  49.     /* System includes. */
  50.  
  51. #include <exec/memory.h>
  52. #include <clib/macros.h>
  53. #include <functions.h>
  54.  
  55. /* StoreTracker is my attempt to fix an apparent bug in ARP 1.3. I have
  56.  * found that the LastTracker kludge (via IoErr()) doesn't work reliably.
  57.  * StoreTracker simply stuffs A1 into D0 and returns . It *MUST* be
  58.  * called immediately after any ARP call which allocates a tracker to
  59.  * assure that the value is correct.
  60.  */
  61.  
  62. #asm
  63.     public    _StoreTracker
  64.  
  65. _StoreTracker:
  66.     move.l    a1,d0
  67.     rts
  68. #endasm
  69.  
  70. struct DefaultTracker *    StoreTracker(VOID);
  71.  
  72. char *            FGetsARP(char *s,LONG length,ARPFileHandle *file);
  73. static LONG        FillARPFileBuffer(ARPFileHandle *file);
  74. static LONG        FlushARPFileBuffer(ARPFileHandle *file);
  75. LONG            FPutsARP(char *s,ARPFileHandle *file);
  76. LONG            ReadARPFile(ARPFileHandle *file,UBYTE *buffer,LONG length);
  77. LONG            CloseARPFile(ARPFileHandle *file);
  78. ARPFileHandle *        OpenARPFile(char *name,LONG accessMode,LONG bytes);
  79. LONG            SeekARPFile(ARPFileHandle *file,LONG position,LONG mode);
  80. LONG            WriteARPFile(ARPFileHandle *file,UBYTE *buffer,LONG length);
  81.  
  82. /*  FUNCTION
  83.  *      FGetsARP - get string from a buffered ARP file.
  84.  *
  85.  *  SYNOPSIS
  86.  *      char *FGetsARP(s, length, file)
  87.  *                     UBYTE         *s;
  88.  *                     LONG           length;
  89.  *                     ARPFileHandle *file;
  90.  *
  91.  *  DESCRIPTION
  92.  *      FGetsARP models the behavior of the "standard" fgets, except that
  93.  *      it is used with a buffered ARP file. The data is read from <file>
  94.  *      and transfered to string <s>. Up to length-1 characters will be
  95.  *      read. Reading is also terminated upon receipt of a newline
  96.  *      or detection of end-of-file. 
  97.  *
  98.  *      If the <file> was opened without a buffer, one of MaxInputBuf
  99.  *      bytes will be allocated.
  100.  *
  101.  *      If end of file or an error is detected, the return value will be
  102.  *      NULL. Otherwise, the return value will be <s>. <s> will be 
  103.  *      terminated with a null byte.
  104.  */
  105.  
  106. char *
  107. FGetsARP(char *s,LONG length,ARPFileHandle *file)
  108. {
  109.     struct DefaultTracker    *tracker;
  110.     LONG             bytesNeeded = length - 1;
  111.     LONG             bytesRead = 0;
  112.     char             c;
  113.     char            *s1 = s;
  114.  
  115.     /* Set string initially empty to protect user from failure to check
  116.      * return value.
  117.      */
  118.  
  119.     *s1 = '\0';    
  120.  
  121.     if(file -> mode != MODE_OLDFILE)
  122.         file -> lastError = ERROR_READ_PROTECTED;
  123.          
  124.     if(file -> lastError)
  125.     {
  126. dangit:        return(NULL);
  127.     }
  128.  
  129.         /* Ohmigosh! No buffer? */
  130.  
  131.     if(!file -> buf)
  132.     {
  133.         file -> buf = ArpAllocMem(MaxInputBuf, MEMF_CLEAR | MEMF_PUBLIC);
  134.  
  135.         tracker = StoreTracker();
  136.  
  137.         if(!file -> buf)
  138.         {
  139.             file -> lastError = ERROR_NO_FREE_STORE;
  140.             goto dangit;
  141.         }
  142.  
  143.         file -> bufSize        = MaxInputBuf;    /* bufLength, bufPos are zero. */
  144.         file -> bufTracker    = tracker;
  145.     }
  146.  
  147.     while(bytesNeeded)
  148.     {
  149.         if(file -> bufPos >= file -> bufLength)
  150.         {
  151.             if(FillARPFileBuffer(file) < 0)
  152.                 goto dangit;
  153.  
  154.             if(!file -> bufLength)
  155.                 break;
  156.         }
  157.  
  158.         c = file -> buf[file -> bufPos++];
  159.  
  160.         ++bytesRead;
  161.  
  162.         if(c == '\n')
  163.             break;
  164.  
  165.         *s1++ = c;
  166.  
  167.         --bytesNeeded; 
  168.     }
  169.  
  170.     *s1 = '\0';
  171.  
  172.     return(bytesRead ? s : NULL);
  173. }
  174.  
  175. /*  FUNCTION
  176.  *      FillARPFileBuffer - read data into ARPFile buffer.
  177.  *
  178.  *  SYNOPSIS
  179.  *      static LONG FillARPFileBuffer(file)
  180.  *                                    ARPFileHandle *file;
  181.  *
  182.  *  DESCRIPTION
  183.  *      Attempt to fill the buffer associated with <file> by reading
  184.  *      data from the associated external file. The return value will
  185.  *      be one of the following:
  186.  *          >0  => number of bytes read
  187.  *           0  => end of file
  188.  *          -1  => a Bad Thing happened (error code in file -> lastError) 
  189.  *
  190.  *      Note: this is a local routine and is thus declared as "static".
  191.  *      Please inform the author if this is a hardship.
  192.  */
  193.  
  194. static LONG
  195. FillARPFileBuffer(ARPFileHandle *file)
  196. {
  197.     /* Remember where we were. The user might want to try error
  198.      * recovery. Of course, this probably isn't enough info, but
  199.      * it's a start.
  200.      */
  201.  
  202.     file -> lastPosition    = Seek(file -> fh, 0L, OFFSET_CURRENT);
  203.     file -> bufPos        = 0;
  204.     file -> bufLength    = Read(file -> fh, file -> buf, file -> bufSize);
  205.  
  206.         /* We got trubble! */
  207.  
  208.     if(file -> bufLength == -1)
  209.         file -> lastError = IoErr();
  210.     else
  211.     {
  212.         if(!file -> bufLength)
  213.             file -> endOfFile = TRUE;
  214.     }
  215.  
  216.     return(file -> bufLength);
  217.  
  218. /*  FUNCTION
  219.  *      FlushARPFileBuffer - write file buffer contents to disk.
  220.  *
  221.  *  SYNOPSIS
  222.  *      static LONG FlushARPFileBuffer(file)
  223.  *                                     ARPFileHandle *file;
  224.  *  DESCRIPTION
  225.  *      FlushARPFileBuffer writes the contents of <file>'s buffer
  226.  *      (if any) to disk and resets the buffer information. The
  227.  *      return value may be any of:
  228.  *
  229.  *          >0 =>   number of bytes written
  230.  *           0 =>   nothing in buffer
  231.  *          <0 =>   negated error code
  232.  *
  233.  *      Note: it is assumed that this function will only be used locally
  234.  *      and therefore need not be public. If you disagree, please contact
  235.  *      the author.
  236.  */
  237.  
  238. static LONG
  239. FlushARPFileBuffer(ARPFileHandle *file)
  240. {
  241.     LONG bytesWritten = 0;
  242.  
  243.     /* This operation is only allowed for output files. */
  244.  
  245.     if(file -> mode != MODE_NEWFILE)
  246.     {
  247.         file -> lastError = ERROR_WRITE_PROTECTED;
  248.  
  249. badstuff:    return(-file -> lastError);
  250.     }
  251.  
  252.     if(file -> lastError)
  253.         goto badstuff; /* Residual error? */
  254.  
  255.     if(file -> bufLength)
  256.     {
  257.         file -> lastPosition = Seek(file -> fh, 0L, OFFSET_CURRENT);
  258.  
  259.         bytesWritten = Write(file -> fh, file -> buf, file -> bufLength);
  260.  
  261.         if(bytesWritten != file -> bufLength)
  262.         {
  263.             file -> lastError = IoErr();
  264.             goto badstuff;
  265.         }
  266.         else
  267.         {
  268.             file -> bufLength    = 0;
  269.             file -> bufPos        = 0;
  270.         }
  271.     }
  272.  
  273.     return(bytesWritten);
  274. }
  275.  
  276. /*  FUNCTION
  277.  *      FPutsARP - write a string to a buffered ARP file.
  278.  *
  279.  *  SYNOPSIS
  280.  *      LONG FPutsARP(s, file)
  281.  *                    char          *s;
  282.  *                    ARPFileHandle *file;
  283.  *
  284.  *  DESCRIPTION
  285.  *      FPutsARP writes the contents of string <s> to the specified <file>.
  286.  *      If successful, it returns 0. On failure, it returns the negated
  287.  *      system error code. 
  288.  */
  289.  
  290. LONG
  291. FPutsARP(char *s,ARPFileHandle *file)
  292. {
  293.     LONG     bytesLeft, bytesUsed;
  294.     char    *s1 = s;
  295.  
  296.     if(file -> mode != MODE_NEWFILE) 
  297.         file -> lastError = ERROR_WRITE_PROTECTED;
  298.  
  299.     if(file -> lastError)
  300.     {
  301. shucks:        return(-file -> lastError);
  302.     }
  303.  
  304.     bytesLeft = strlen(s);
  305.  
  306.     /* Attempt to be smart about this transfer. Copy the string to the
  307.      * buffer in chunks. There is a possibility that the string is bigger
  308.      * than the size of the buffer.
  309.      */
  310.  
  311.     while(bytesLeft)
  312.     {
  313.         if(file -> bufPos >= file -> bufSize)
  314.         {
  315.             if(FlushARPFileBuffer(file) <= 0)
  316.                 goto shucks;
  317.         }
  318.  
  319.         bytesUsed = MIN(file -> bufSize - file -> bufPos, bytesLeft);
  320.  
  321.         CopyMem(s1, &file -> buf[file -> bufPos], bytesUsed);
  322.  
  323.         s1 += bytesUsed;
  324.  
  325.         file -> bufLength = (file -> bufPos += bytesUsed);
  326.  
  327.         bytesLeft -= bytesUsed;
  328.     }
  329.  
  330.     return(0);
  331. }
  332.  
  333. /*  FUNCTION
  334.  *      ReadARPFile - read from a buffered ARP file.
  335.  *
  336.  *  SYNOPSIS
  337.  *      LONG ReadARPFile(file, buffer, length)
  338.  *                       ARPFile *file;
  339.  *                       UBYTE   *buffer;
  340.  *                       LONG    length;
  341.  *
  342.  *  DESCRIPTION
  343.  *      ReadARPFile attempts to read <length> bytes from <file>, transferring
  344.  *      the data to <buffer>. 
  345.  *
  346.  *      The return value may be any of the following:
  347.  *
  348.  *          >0      number of bytes transferred
  349.  *          0       end of file
  350.  *         <0       negated error code
  351.  *
  352.  *      Note: if the lastError field of the <file> descriptor contains a
  353.  *            non-zero value, its negated value will be returned and no
  354.  *            attempt will be made to read the file. If you attempt error
  355.  *            recovery, you must clear this field to zero.
  356.  */
  357.  
  358. LONG
  359. ReadARPFile(ARPFileHandle *file,UBYTE *buffer,LONG length)
  360. {
  361.     LONG    bytesLeft;
  362.     LONG    bytesRead = 0;
  363.     LONG    needBytes = length;
  364.     LONG    pos = 0;
  365.     LONG    usedBytes = 0;
  366.  
  367.         /* Prevent read if this file opened for writing. */
  368.  
  369.     if(file -> mode != MODE_OLDFILE)
  370.         file -> lastError = ERROR_READ_PROTECTED;
  371.  
  372.         /* Have residual error? */
  373.  
  374.     if(file -> lastError)
  375.     {
  376. boofar:        return(-file -> lastError);
  377.     }
  378.  
  379.         /* No buffer? */
  380.  
  381.     if(!file -> buf )
  382.     {
  383.         bytesRead = Read(file -> fh, buffer, length);
  384.  
  385.         if(bytesRead == -1)
  386.         {
  387.             file -> lastError = IoErr();
  388.             goto boofar;
  389.         }
  390.     }
  391.     else
  392.     {
  393.         while(needBytes)
  394.         {
  395.             if(file -> bufLength - file -> bufPos <= 0)
  396.             {
  397.                 if(FillARPFileBuffer(file) == -1)
  398.                     goto boofar;
  399.  
  400.                 if(!file -> bufLength)
  401.                     break;
  402.             }
  403.  
  404.             bytesLeft = file -> bufLength - file -> bufPos;
  405.  
  406.             usedBytes = MIN(bytesLeft, length);
  407.  
  408.             CopyMem(&file -> buf[file -> bufPos], &buffer[pos], usedBytes);
  409.  
  410.             file -> bufPos += usedBytes;
  411.  
  412.             pos += usedBytes;
  413.  
  414.             bytesRead += usedBytes;
  415.             needBytes -= usedBytes;
  416.         }
  417.     }
  418.  
  419.     return(bytesRead);
  420. }
  421.  
  422. /*  FUNCTION
  423.  *      CloseARPFile - close buffered ARP file.
  424.  *
  425.  *  SYNOPSIS
  426.  *      LONG CloseARPFile(file)
  427.  *                        ARPFileHandle *file;
  428.  *
  429.  *  DESCRIPTION
  430.  *      CloseARPFile closes the file described by <file> which MUST have
  431.  *      been opened by OpenARPFile. It releases all tracked items
  432.  *      associated with <file>, as well.
  433.  *
  434.  *      The return value is only meaningful for output files. If, upon
  435.  *      flushing the buffer, a write error is detected, a system error
  436.  *      code (ERROR_DISK_FULL, etc.) will be returned.
  437.  */
  438.  
  439. LONG
  440. CloseARPFile(ARPFileHandle *file)
  441. {
  442.     LONG result = 0;
  443.  
  444.         /* Just in case... */
  445.  
  446.     if(file)
  447.     {
  448.         if(file -> fileTracker)
  449.         {
  450.             /* Any left-over stuff in the buffer? If so, we must flush
  451.              * it to disk. However, if an error was detected in the
  452.              * previous operation, punt. 
  453.              */
  454.  
  455.             if((file -> mode == MODE_NEWFILE) && ! file -> lastError && file -> bufLength)
  456.             {
  457.                 if(Write(file -> fh, file -> buf, file -> bufLength) != file -> bufLength)
  458.                     result = IoErr();
  459.             }
  460.  
  461.             FreeTrackedItem(file -> fileTracker);
  462.         }
  463.  
  464.         if(file -> bufTracker)
  465.             FreeTrackedItem(file -> bufTracker);
  466.  
  467.         FreeTrackedItem(file -> myTracker);
  468.     }
  469.  
  470.     return(result);
  471. }
  472.  
  473. /*  FUNCTION
  474.  *      OpenARPFile - open a buffered ARP file
  475.  *
  476.  *  SYNOPSIS
  477.  *      struct MRARPFile *OpenARPFile(name, accessMode, bytes)
  478.  *                                    char *name;
  479.  *                                    LONG accessMode, bytes;
  480.  *
  481.  *  DESCRIPTION
  482.  *      OpenARPFile opens the file <name>, with the given <accessMode>
  483.  *      (MODE_OLDFILE, MODE_NEWFILE only!) for buffered access. The
  484.  *      size of the local buffer is specified by <bytes>.
  485.  *
  486.  *      A zero value for <bytes> is OK and is sometimes appropriate, as
  487.  *      when performing file copy operations, etc. However, if a file
  488.  *      opened with a zero length buffer is then passed to the
  489.  *      FGetsARP function, "spontaneous buffer allocation" will occur.
  490.  *      No biggy, really, just something you should know. The ARP constant,
  491.  *      MaxInputBuf will be used for the buffer size, in this case.
  492.  *
  493.  *      If successful, a pointer to the file tracking structure is 
  494.  *      returned. Otherwise, the return value will be NULL.
  495.  *
  496.  *      OpenARPFile uses full resource tracking for all information
  497.  *      associated with the file.
  498.  *
  499.  */
  500.  
  501. ARPFileHandle *
  502. OpenARPFile(char *name,LONG accessMode,LONG bytes)
  503. {
  504.     struct DefaultTracker    *lastTracker;
  505.     ARPFileHandle        *theFile = NULL;
  506.     BPTR             fh;
  507.  
  508.         /* This package does not support READ/WRITE access! */
  509.  
  510.     if((accessMode != MODE_OLDFILE) && (accessMode != MODE_NEWFILE))
  511.         return(NULL);
  512.  
  513.     /* 
  514.      * Note: I'm using ArpAllocMem vs. ArpAlloc here because it appears
  515.      * that ArpAlloc gives me a bad tracker pointer (?).
  516.      */
  517.  
  518.     theFile = ArpAllocMem((LONG) sizeof(ARPFileHandle), MEMF_PUBLIC | MEMF_CLEAR);
  519.  
  520.     lastTracker = StoreTracker();
  521.  
  522.     if(theFile)
  523.     {
  524.         theFile -> myTracker = lastTracker;
  525.         theFile -> mode = accessMode;
  526.  
  527.         fh = ArpOpen(name, accessMode);
  528.  
  529.         lastTracker = StoreTracker();
  530.  
  531.         if(!fh)
  532.         {
  533. fungu:            CloseARPFile(theFile);    /* Don't worry - it's "smart". */
  534.             theFile = NULL;
  535.  
  536.             return(NULL);        /* This one was missing -Olsen */
  537.         }
  538.  
  539.         theFile -> fileTracker    = lastTracker;
  540.         theFile -> fh        = fh;
  541.  
  542.             /* Does user want a buffer? */
  543.  
  544.         if(bytes)
  545.         {
  546.             theFile -> buf = ArpAllocMem(bytes, MEMF_PUBLIC | MEMF_CLEAR);
  547.  
  548.             lastTracker = StoreTracker();
  549.  
  550.             if(!theFile -> buf)
  551.                 goto fungu;
  552.  
  553.             theFile -> bufSize    = bytes;
  554.             theFile -> bufTracker    = lastTracker;
  555.         }
  556.     }
  557.  
  558.     return(theFile);
  559. }
  560.  
  561. /*  FUNCTION
  562.  *      SeekARPFile - move to new logical position in file.
  563.  *
  564.  *  SYNOPSIS
  565.  *      LONG SeekARPFile(file, position, mode)
  566.  *                       ARPFileHandle *file;
  567.  *                       LONG           position;
  568.  *                       LONG           mode;
  569.  *
  570.  *  DESCRIPTION
  571.  *      SeekARPFile attempts to position the <file> to a new logical
  572.  *      position or report it's current position. The <position>
  573.  *      parameter represets a signed offset. The <mode> parameter may
  574.  *      be one of:
  575.  *
  576.  *          OFFSET_BEGINNING (-1) => from beginning of file
  577.  *          OFFSET_CURRENT (0)    => from current position
  578.  *          OFFSET_END (-1)       => from end of file
  579.  *
  580.  *      On output files, the current buffer contents, if any, are
  581.  *      written to disk.
  582.  *
  583.  *      The return value will either be a positive displacement >=0) or
  584.  *      a negated system error code.
  585.  */
  586.  
  587. LONG
  588. SeekARPFile(ARPFileHandle *file,LONG position,LONG mode)
  589. {
  590.     LONG newPosition;
  591.  
  592.     if(file -> mode == MODE_NEWFILE && file -> bufLength)
  593.     {
  594.         if(FlushARPFileBuffer(file) < 0)
  595.         {
  596. farboo:            return(-file -> lastError);
  597.         }
  598.     }
  599.  
  600.         /* Remember our last position. Seek(file,...) fixed
  601.          * to use Seek(file -> fh,...) -Olsen.
  602.          */
  603.  
  604.     file -> lastPosition = Seek(file -> fh, 0L, OFFSET_CURRENT);
  605.  
  606.     if((newPosition = Seek(file -> fh, position, mode)) == -1)
  607.     {
  608.         file -> lastError = IoErr();
  609.         goto farboo;
  610.     }
  611.  
  612.     return(newPosition);
  613. }  
  614.  
  615. /*  FUNCTION
  616.  *      WriteARPFile - write data to a buffered ARP file.
  617.  *
  618.  *  SYNOPSIS
  619.  *      LONG WriteARPFile(file, buffer, length)
  620.  *                        ARPFileHandle *file;
  621.  *                        UBYTE         *buffer;
  622.  *                        LONG          length;
  623.  *
  624.  *  DESCRIPTION
  625.  *      WriteARPFile attempts to write <length> bytes from <buffer> to
  626.  *      the buffered ARP file, <file>. The file MUST have been opened
  627.  *      with OpenARPFile for MODE_NEWFILE access.
  628.  *
  629.  *      WriteARPFile will not write to a file if the lastError field is
  630.  *      non-zero, or if the file was opened for input.
  631.  *
  632.  *      If successful, WriteARPFile will return the <length> parameter.
  633.  *      Otherwise, it will return a negated system error code.
  634.  */
  635.  
  636. LONG
  637. WriteARPFile(ARPFileHandle *file,UBYTE *buffer,LONG length)
  638. {
  639.     LONG    bufferBytes;
  640.     LONG    bytesLeft = length;
  641.     LONG    pos = 0;
  642.     LONG    usedBytes = 0;
  643.  
  644.     if(file -> mode != MODE_NEWFILE)
  645.         file -> lastError = ERROR_WRITE_PROTECTED;
  646.  
  647.         /* Catches mode and residual errors. */
  648.  
  649.     if(file -> lastError)
  650.     {
  651. sumbidge:    return(-file -> lastError);
  652.     }
  653.  
  654.         /* No buffer? */
  655.  
  656.     if(!file -> buf)
  657.     {
  658.         if(Write(file -> fh, buffer, length) != length)
  659.         {
  660.             file -> lastError = IoErr();
  661.             goto sumbidge;
  662.         }
  663.     }
  664.     else
  665.     {
  666.         while(bytesLeft)
  667.         {
  668.                 /* Need to flush the file's buffer? */
  669.  
  670.             if(file -> bufPos >= file -> bufSize)
  671.                 if(FlushARPFileBuffer(file) < 0)
  672.                     goto sumbidge;
  673.  
  674.             bufferBytes = file -> bufSize - file -> bufPos;
  675.  
  676.             usedBytes = MIN(bufferBytes, bytesLeft);
  677.  
  678.             CopyMem(&buffer[pos], &file -> buf[file -> bufPos], usedBytes);
  679.  
  680.             file -> bufLength = (file -> bufPos += usedBytes);
  681.  
  682.             pos += usedBytes;
  683.  
  684.             bytesLeft -= usedBytes;
  685.         }
  686.     }
  687.  
  688.     return(length);
  689. }
  690.